#include <benchmark/benchmark.h>

#include <cassert>
#include <iostream>

#include "helper/sqlite/AutoDB.h"
#include "helper/sqlite/Statement.h"

class EmptyKvTableFixture : public ::benchmark::Fixture {
public:
  void SetUp(const ::benchmark::State & state) override{
    action("create table if not exists tab1 (one integer, two text);");
  }
  void TearDown(const ::benchmark::State & state) override{
    action("drop table if exists tab1;");
    db().destroy();
  }

	helper::sqlite::IDB & db() const{
    return _autodb.ref();
  }

protected:
	void query(std::string & sql){
    std::error_code ec;
    auto && stmt = db().createStatement(sql, ec);
    assert(stmt && !ec);
		auto && rs = stmt->query();
		assert(rs);	
	}

	void insert(size_t count){
		std::string sql = "insert into tab1 values(?, '12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678');";
    std::error_code ec;
    auto && stmt = db().createStatement(sql, ec);
		assert(stmt && !ec);

		for(size_t i = 0; i < count; ++i){
			assert(!stmt->reset());
			assert(!stmt->clearBindings());
			assert(!stmt->intBind(1, i));
			assert(stmt->execute());
		}
	}

	void execute(const std::string & sql) {
    std::error_code ec;
    auto && stmt = db().createStatement(sql, ec);
    assert(stmt && !ec);
		assert(stmt->execute());
	}
	size_t executeRetAffectRows(const std::string & sql) {
    std::error_code ec;
    auto && stmt = db().createStatement(sql, ec);
    assert(stmt && !ec);
		auto && r = stmt->execute();
		return r.affectRows();
	}
	
  void action(const std::string & sql){
    std::error_code ec;
    auto && stmt = db().createStatement(sql, ec);
    assert(stmt && !ec);
    assert(stmt->action());
  }
  void action_bind(const std::string & sql, const std::string & str){
    std::error_code ec;
    auto && stmt = db().createStatement(sql, ec);
    assert(stmt && !ec);
    assert(0 == stmt->textBind(1, str).value());
    assert(stmt->action());
  }

	size_t count(const std::string & countSql = "") {
		std::string sql = countSql.empty() ? std::string("select count(1) from tab1") : countSql;
    std::error_code ec;
    auto && stmt = db().createStatement(sql, ec);
    assert(stmt && !ec);
		auto && rs = stmt->query();
		assert(rs);
		return rs.values(0)->integer();
	}
protected:
	helper::sqlite::AutoDB _autodb;
};


class FileDBWithEmptyTable : public EmptyKvTableFixture {
public:
  void SetUp(const ::benchmark::State & state) override{
    _autodb = std::move(helper::sqlite::AutoDB::make_file(dbname));

    EmptyKvTableFixture::SetUp(state);
  }
  void TearDown(const ::benchmark::State & state) override{
    EmptyKvTableFixture::TearDown(state);
  }
private:
	std::string dbname = "test-file.db";
};

class MemoryDBWithEmptyTable : public EmptyKvTableFixture {
public:
  void SetUp(const ::benchmark::State & state) override{
    _autodb = std::move(helper::sqlite::AutoDB::make_memory());

    EmptyKvTableFixture::SetUp(state);
  }
  void TearDown(const ::benchmark::State & state) override{
    EmptyKvTableFixture::TearDown(state);
  }
};


